package com.jingzhongbao.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;

import com.jingzhongbao.springboot.filter.AfterCsrfFilter;
import com.jingzhongbao.springboot.filter.BeforeLoginFilter;
import com.jingzhongbao.springboot.service.CustomUserService;
/**
 * @author 中宝
 * Security配置类
 * @date 2018/10/25 下午14:17
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法安全设置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder; //等同于.passwordEncoder(new BCryptPasswordEncoder())
    
    @Bean
    CustomUserService customUserService() {
        return new CustomUserService();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 使用 BCrypt加密  等同于.passwordEncoder(new BCryptPasswordEncoder())
    }
    @Bean
    public AuthenticationProvider authenticationProvider() {// 等同于.passwordEncoder(new BCryptPasswordEncoder())
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(customUserService());
        authenticationProvider.setPasswordEncoder(passwordEncoder); // 设置密码加密方式
        return authenticationProvider;
    }
    /**
     * 自定义配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
		http.
			//http.authorizeRequests()方法有多个子节点，每个macher按照他们的声明顺序执行
			authorizeRequests()
				//我们指定任何用户都可以访问多个URL的模式。
             	//任何用户都可以访问以"/","/login", 或者 "/index"开头的URL。
				.antMatchers("/","/login", "/index").permitAll()
				//以 "/admin/" 开头的URL只能让拥有 "ROLE_ADMIN"角色的用户访问。
				//请注意我们使用 hasRole 方法，没有使用 "ROLE_" 前缀。               
				.antMatchers("/admin/**").hasRole("ADMIN")
				//任何以"/db/" 开头的URL需要同时具有 "ROLE_ADMIN" 和 "ROLE_DBA"权限的用户才可以访问。
				//和上面一样我们的 hasRole 方法也没有使用 "ROLE_" 前缀。              
				.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
				//任何以"/db/" 开头的URL只需要拥有 "ROLE_ADMIN" 和 "ROLE_DBA"其中一个权限的用户才可以访问。
				//和上面一样我们的 hasRole 方法也没有使用 "ROLE_" 前缀。          
				.antMatchers("/db/**").hasAnyRole("ADMIN", "DBA")
				//尚未匹配的任何URL都要求用户进行身份验证
				.anyRequest().authenticated()
				.and()
				// 指定登录页通过formlogin方法登录，并设置登录url为/login
				.formLogin().loginPage("/login")
				// 登录成功后默认跳转到/index页面
				.defaultSuccessUrl("/index")
				//指定登录失败后跳转到/login?error页面
		        .failureUrl("/login?error")
		        .permitAll()
		        .and()
				//开启cookie储存用户信息，并设置有效期为14天，指定cookie中的密钥
		        .rememberMe().tokenValiditySeconds(1209600).key("mykey")
				.and()
				.logout()
				//指定登出的url为/logout
				.logoutUrl("/logout")
				//指定退出登录成功之后的默认url是/index
				.logoutSuccessUrl("/index")
				.permitAll();
		// 在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter
        http.addFilterBefore(new BeforeLoginFilter(), UsernamePasswordAuthenticationFilter.class);
        // 在 CsrfFilter 后添加 AfterCsrfFilter
        http.addFilterAfter(new AfterCsrfFilter(), CsrfFilter.class);
    }
    /**
     * 认证信息管理
     *
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    	//设置根据用户名获取用户信息的自定义customUserService类,该类从数据库中读用用户信息
    	auth.userDetailsService(customUserService()).passwordEncoder(new BCryptPasswordEncoder());
    	//加密 等同于.passwordEncoder(new BCryptPasswordEncoder())
        //auth.authenticationProvider(authenticationProvider()); 
    }
}
